﻿using System;
using System.Collections.Generic;
using System.Text;
using RaisingStudio.Data.Expressions;
using RaisingStudio.Common.Web;

namespace RaisingStudio.Data.Common.Builders.Expressions
{
    public class SharePointConditionExpressionCommandBuilder : ConditionExpressionCommandBuilder
    {
        public SharePointConditionExpressionCommandBuilder(RaisingStudio.Data.Expressions.ConditionExpression conditionExpression, Dictionary<string, System.Data.DbType> dbTypes, RaisingStudio.Data.Expressions.TableExpression tableExpression)
            : base(conditionExpression, dbTypes, tableExpression)
        {
        }

        public override CommonCommand GetExpressionCommand()
        {
            if (this.ConditionExpression != null)
            {
                // TODO: 
                CommonCommand commonCommand = new CommonCommand();
                System.Text.StringBuilder commandTextStringBuilder = new StringBuilder();
                #region Where
                if (ConditionExpression.ExpressionElement != null)
                {
                    commandTextStringBuilder.Append("<Where>");
                    #region parse Epxression
                    ExpressionElement expressionElement = ConditionExpression.ExpressionElement;
                    AppendQueryExpressionCommand(ref commandTextStringBuilder, expressionElement, DbTypes, TableExpression);
                    #endregion
                    commandTextStringBuilder.Append("</Where>");
                }
                #endregion
                #region OrderBy
                if (ConditionExpression.HasSortingExpressions)
                {
                    commandTextStringBuilder.Append("<OrderBy>");
                    foreach (SortingExpression sortingExpression in ConditionExpression.SortingExpressions)
                    {
                        string sortingExpressionString = GetQuerySortingExpressionString(sortingExpression);
                        commandTextStringBuilder.Append(sortingExpressionString);
                    }
                    commandTextStringBuilder.Append("</OrderBy>");
                }
                #endregion

                string commandText = commandTextStringBuilder.ToString();
                commonCommand.CommandText = commandText;
                return commonCommand;
            }
            else
            {
                return null;
            }
        }

        private void AppendQueryExpressionCommand(ref StringBuilder commandTextStringBuilder, ExpressionElement expressionElement, Dictionary<string, System.Data.DbType> dbTypes, TableExpression tableExpression)
        {
            // TODO:
            if (expressionElement is OperationExpression)
            {
                // Operation expression
                OperationExpression operationExpression = expressionElement as OperationExpression;
                RaisingStudio.Data.CommonCommand operationExpressionCommand = GetQueryOperationExpressionCommand(operationExpression, dbTypes, tableExpression);
                AppendQueryExpressionCommand(ref commandTextStringBuilder, operationExpressionCommand);
            }
            else if (expressionElement is RelationExpression)
            {
                // Operation expression
                RelationExpression relationExpression = expressionElement as RelationExpression;
                if (!relationExpression.IsFormatted)
                {
                    RelationExpression formattedRelationExpression = FormatRelationExpression(relationExpression);
                    RaisingStudio.Data.CommonCommand relationExpressionCommand = GetQueryRelationExpressionCommand(formattedRelationExpression, dbTypes, tableExpression);
                    AppendQueryExpressionCommand(ref commandTextStringBuilder, relationExpressionCommand);
                }
                else
                {
                    RaisingStudio.Data.CommonCommand relationExpressionCommand = GetQueryRelationExpressionCommand(relationExpression, dbTypes, tableExpression);
                    AppendQueryExpressionCommand(ref commandTextStringBuilder, relationExpressionCommand);
                }
            }
            else
            {
                // TODO: Unknown expression
                throw new NotSupportedException("Unknown Epxression Type.");
            }
        }

        private RelationExpression FormatRelationExpression(RelationExpression relationExpression)
        {
            RelationExpression formattedRelationExpression = new RelationExpression();
            formattedRelationExpression.IsFormatted = true;
            ExpressionElement expressionElement = relationExpression.ExpressionElement;
            if (relationExpression.HasChildren)
            {
                if (expressionElement != null)
                {
                    formattedRelationExpression.RelationOperator = relationExpression.RelationOperator;
                    formattedRelationExpression.Children.Add(new RelationExpression(expressionElement));
                    FormatChildExpression(ref formattedRelationExpression, relationExpression);
                }
                else
                {
                    FormatChildExpression(ref formattedRelationExpression, relationExpression);
                }
            }
            else
            {
                if (expressionElement != null)
                {
                    if (relationExpression.RelationOperator == RelationOperator.Not)
                    {
                        throw new NotImplementedException();
                    }
                    else
                    {
                        // TODO:
                        formattedRelationExpression.ExpressionElement = expressionElement;
                    }
                }
                else
                {
                    // TODO: Epxression Is null.
                }
            }

            return formattedRelationExpression;
        }

        private void FormatChildExpression(ref RelationExpression formattedRelationExpression, RelationExpression relationExpression)
        {
            RelationExpression formattedRelationExpression_FormattedChildExpression = new RelationExpression();
            formattedRelationExpression_FormattedChildExpression.IsFormatted = true;
            foreach (RelationExpression childExpression in relationExpression.Children)
            {
                RelationExpression formattedChildExpression = FormatRelationExpression(childExpression);
                if (childExpression.RelationOperator == RelationOperator.None)
                {
                    formattedRelationExpression_FormattedChildExpression.Children.Add(formattedChildExpression);
                }
                else
                {
                    if ((formattedRelationExpression_FormattedChildExpression.HasChildren) && (formattedRelationExpression_FormattedChildExpression.RelationOperator == RelationOperator.None) && (formattedRelationExpression_FormattedChildExpression.Children.Count == 1))
                    {
                        formattedRelationExpression_FormattedChildExpression.Children.Add(formattedChildExpression);
                        formattedRelationExpression_FormattedChildExpression.RelationOperator = childExpression.RelationOperator;
                    }
                    else
                    {
                        formattedRelationExpression_FormattedChildExpression = new RelationExpression(formattedRelationExpression_FormattedChildExpression, childExpression.RelationOperator, formattedChildExpression, true);
                    }
                }
            }
            if (formattedRelationExpression.RelationOperator == RelationOperator.None && !formattedRelationExpression.HasChildren)
            {
                formattedRelationExpression = formattedRelationExpression_FormattedChildExpression;
            }
            else
            {
                formattedRelationExpression.Children.Add(formattedRelationExpression_FormattedChildExpression);
            }
        }

        private CommonCommand GetQueryOperationExpressionCommand(OperationExpression operationExpression, Dictionary<string, System.Data.DbType> dbTypes, TableExpression tableExpression)
        {
            RaisingStudio.Data.CommonCommand commonCommand = new CommonCommand();
            System.Text.StringBuilder commandTextStringBuilder = new StringBuilder();
            // TODO:
            #region Operator
            switch (operationExpression.Operator)
            {
                case ElementOperator.Equal:
                    {
                        commandTextStringBuilder.Append("<Eq>");
                        commandTextStringBuilder.Append(GetQueryOperationExpressionElementExpressionString(dbTypes, operationExpression));
                        commandTextStringBuilder.Append(GetQueryOperationExpressionValueExpressionString(dbTypes, operationExpression));
                        commandTextStringBuilder.Append("</Eq>");
                        break;
                    }
                case ElementOperator.NotEqual:
                    {
                        commandTextStringBuilder.Append("<Neq>");
                        commandTextStringBuilder.Append(GetQueryOperationExpressionElementExpressionString(dbTypes, operationExpression));
                        commandTextStringBuilder.Append(GetQueryOperationExpressionValueExpressionString(dbTypes, operationExpression));
                        commandTextStringBuilder.Append("</Neq>");
                        break;
                    }
                case ElementOperator.Greater:
                    {
                        commandTextStringBuilder.Append("<Gt>");
                        commandTextStringBuilder.Append(GetQueryOperationExpressionElementExpressionString(dbTypes, operationExpression));
                        commandTextStringBuilder.Append(GetQueryOperationExpressionValueExpressionString(dbTypes, operationExpression));
                        commandTextStringBuilder.Append("</Gt>");
                        break;
                    }
                case ElementOperator.GreaterOrEqual:
                    {
                        commandTextStringBuilder.Append("<Geq>");
                        commandTextStringBuilder.Append(GetQueryOperationExpressionElementExpressionString(dbTypes, operationExpression));
                        commandTextStringBuilder.Append(GetQueryOperationExpressionValueExpressionString(dbTypes, operationExpression));
                        commandTextStringBuilder.Append("</Geq>");
                        break;
                    }
                case ElementOperator.Less:
                    {
                        commandTextStringBuilder.Append("<Lt>");
                        commandTextStringBuilder.Append(GetQueryOperationExpressionElementExpressionString(dbTypes, operationExpression));
                        commandTextStringBuilder.Append(GetQueryOperationExpressionValueExpressionString(dbTypes, operationExpression));
                        commandTextStringBuilder.Append("</Lt>");
                        break;
                    }
                case ElementOperator.LessOrEqual:
                    {
                        commandTextStringBuilder.Append("<Leq>");
                        commandTextStringBuilder.Append(GetQueryOperationExpressionElementExpressionString(dbTypes, operationExpression));
                        commandTextStringBuilder.Append(GetQueryOperationExpressionValueExpressionString(dbTypes, operationExpression));
                        commandTextStringBuilder.Append("</Leq>");
                        break;
                    }
                case ElementOperator.IsNull:
                    {
                        commandTextStringBuilder.Append("<IsNull>");
                        commandTextStringBuilder.Append(GetQueryOperationExpressionElementExpressionString(dbTypes, operationExpression));
                        commandTextStringBuilder.Append("</IsNull>");
                        break;
                    }
                case ElementOperator.IsNotNull:
                    {
                        commandTextStringBuilder.Append("<IsNotNull>");
                        commandTextStringBuilder.Append(GetQueryOperationExpressionElementExpressionString(dbTypes, operationExpression));
                        commandTextStringBuilder.Append("</IsNotNull>");
                        break;
                    }
                case ElementOperator.Like:
                    {
                        string valueString = operationExpression.Value as string;
                        if (!string.IsNullOrEmpty(valueString))
                        {
                            if (valueString.EndsWith("%"))
                            {
                                string type = GetQueryCommonParameterDbType(dbTypes, operationExpression);
                                if (valueString.StartsWith("%"))
                                {
                                    commandTextStringBuilder.Append("<Contains>");
                                    commandTextStringBuilder.Append(GetQueryOperationExpressionElementExpressionString(dbTypes, operationExpression));
                                    commandTextStringBuilder.Append(GetQueryValueExpressionString(type, valueString.Substring(1, valueString.Length - 2)));
                                    commandTextStringBuilder.Append("</Contains>");
                                }
                                else
                                {
                                    commandTextStringBuilder.Append("<BeginsWith>");
                                    commandTextStringBuilder.Append(GetQueryOperationExpressionElementExpressionString(dbTypes, operationExpression));
                                    commandTextStringBuilder.Append(GetQueryValueExpressionString(type, valueString.Substring(0, valueString.Length - 1)));
                                    commandTextStringBuilder.Append("</BeginsWith>");
                                }
                            }
                            else
                            {
                                throw new NotImplementedException();
                            }
                        }
                        break;
                    }
                case ElementOperator.NotLike:
                    {
                        throw new NotImplementedException();
                        break;
                    }
                case ElementOperator.Between:
                    {
                        throw new NotImplementedException();
                        break;
                    }
                case ElementOperator.NotBetween:
                    {
                        throw new NotImplementedException();
                        break;
                    }
                case ElementOperator.In:
                    {
                        throw new NotImplementedException();
                        break;
                    }
                case ElementOperator.NotIn:
                    {
                        throw new NotImplementedException();
                        break;
                    }
            }
            #endregion
            string commandText = commandTextStringBuilder.ToString();
            commonCommand.CommandText = commandText;
            return commonCommand;
        }
        private string GetQueryOperationExpressionElementExpressionString(Dictionary<string, System.Data.DbType> dbTypes, OperationExpression operationExpression)
        {
            #region Element
            if (operationExpression.Element is ColumnExpression)
            {
                // Column expression.
                ColumnExpression columnExpression = operationExpression.Element as ColumnExpression;
                return string.Format("<FieldRef Name='{0}'/>", columnExpression);
            }
            else if (operationExpression.Element is FunctionExpression)
            {
                // Function expression
                throw new NotImplementedException();
            }
            else if (operationExpression.Element is BinaryExpression)
            {
                // expression
                throw new NotImplementedException();
            }
            else if (operationExpression.Element is ValueExpression)
            {
                throw new NotImplementedException();
            }
            else
            {
                // TODO: Unknown expression
            }
            #endregion
            return null;
        }
        private string GetQueryOperationExpressionValueExpressionString(Dictionary<string, System.Data.DbType> dbTypes, OperationExpression operationExpression)
        {
            #region Value
            if ((operationExpression.Operator != ElementOperator.IsNull) && (operationExpression.Operator != ElementOperator.IsNotNull))
            {
                #region 元素
                if (operationExpression.Value is ColumnExpression)
                {
                    // Column expression.
                    throw new NotImplementedException();
                }
                else if (operationExpression.Value is FunctionExpression)
                {
                    // Function expression
                    throw new NotImplementedException();
                }
                else if (operationExpression.Value is BinaryExpression)
                {
                    // expression
                    throw new NotImplementedException();
                }
                #endregion
                else
                {
                    object value = operationExpression.Value;
                    string type = GetQueryCommonParameterDbType(dbTypes, operationExpression);
                    return GetQueryValueExpressionString(type, value);
                }
            }
            #endregion
            return null;
        }

        private string GetQueryValueExpressionString(string type, object value)
        {
            string valueString = null;
            if (value != null)
            {
                if (value is DateTime)
                {
                    DateTime dateTime = (DateTime)value;
                    valueString = CreateISO8601DateTimeFromSystemDateTime(dateTime);
                    if (dateTime.Kind == DateTimeKind.Utc)
                    {
                        return string.Format("<Value Type='{0}' IncludeTimeValue='TRUE' StorageTZ='TRUE'>{1}</Value>", type, valueString);
                    }
                    else
                    {
                        return string.Format("<Value Type='{0}' IncludeTimeValue='TRUE'>{1}</Value>", type, valueString);
                    }
                }
                else if (value is string)
                {
                    // TODO: html encoding.                            
                    valueString = HttpUtility.HtmlEncode(value as string);
                }
                else
                {
                    // TODO: html encoding.
                    valueString = HttpUtility.HtmlEncode(value.ToString());
                }
            }
            else
            {
                // TODO:
            }
            // values.
            return string.Format("<Value Type='{0}'>{1}</Value>", type, valueString);
        }

        private string CreateISO8601DateTimeFromSystemDateTime(DateTime dateTime)
        {
            return dateTime.ToString("yyyy-MM-ddTHH:mm:ssZ");
        }

        private string GetQueryCommonParameterDbType(Dictionary<string, System.Data.DbType> dbTypes, OperationExpression operationExpression)
        {
            System.Data.DbType dbType = GetCommonParameterDbType(dbTypes, operationExpression);
            switch (dbType)
            {
                case System.Data.DbType.String:
                    {
                        return "Text";
                        break;
                    }
                case System.Data.DbType.Int32:
                    {
                        return "Integer";
                        break;
                    }
                case System.Data.DbType.DateTime:
                    {
                        return "DateTime";
                        break;
                    }
                case System.Data.DbType.Boolean:
                    {
                        return "Boolean";
                        break;
                    }
                case System.Data.DbType.Decimal:
                    {
                        return "Number";
                        break;
                    }
                case System.Data.DbType.Guid:
                    {
                        return "Guid";
                        break;
                    }
                default:
                    {
                        return "Text";
                        break;
                    }
            }
            return "Text";

        }

        private CommonCommand GetQueryRelationExpressionCommand(RelationExpression relationExpression, Dictionary<string, System.Data.DbType> dbTypes, TableExpression tableExpression)
        {
            RaisingStudio.Data.CommonCommand commonCommand = new CommonCommand();
            System.Text.StringBuilder commandTextStringBuilder = new StringBuilder();

            ExpressionElement expressionElement = relationExpression.ExpressionElement;
            if (relationExpression.HasChildren)
            {
                if (expressionElement != null)
                {
                    switch (relationExpression.RelationOperator)
                    {
                        case RelationOperator.And:
                            {
                                commandTextStringBuilder.Append("<And>");
                                AppendQueryExpressionCommand(ref commandTextStringBuilder, expressionElement, dbTypes, tableExpression);
                                AppendQueryChildExpressionCommand(ref commandTextStringBuilder, relationExpression, dbTypes, tableExpression);
                                commandTextStringBuilder.Append("</And>");
                                break;
                            }
                        case RelationOperator.Or:
                            {
                                commandTextStringBuilder.Append("<Or>");
                                AppendQueryExpressionCommand(ref commandTextStringBuilder, expressionElement, dbTypes, tableExpression);
                                AppendQueryChildExpressionCommand(ref commandTextStringBuilder, relationExpression, dbTypes, tableExpression);
                                commandTextStringBuilder.Append("</Or>");
                                break;
                            }
                        default:
                            {
                                // TODO: Other Relation.
                                break;
                            }
                    }
                }
                else
                {
                    switch (relationExpression.RelationOperator)
                    {
                        case RelationOperator.And:
                            {
                                commandTextStringBuilder.Append("<And>");
                                AppendQueryChildExpressionCommand(ref commandTextStringBuilder, relationExpression, dbTypes, tableExpression);
                                commandTextStringBuilder.Append("</And>");
                                break;
                            }
                        case RelationOperator.Or:
                            {
                                commandTextStringBuilder.Append("<Or>");
                                AppendQueryChildExpressionCommand(ref commandTextStringBuilder, relationExpression, dbTypes, tableExpression);
                                commandTextStringBuilder.Append("</Or>");
                                break;
                            }
                        default:
                            {
                                AppendQueryChildExpressionCommand(ref commandTextStringBuilder, relationExpression, dbTypes, tableExpression);
                                break;
                            }
                    }
                }
            }
            else
            {
                if (expressionElement != null)
                {
                    if (relationExpression.RelationOperator == RelationOperator.Not)
                    {
                        throw new NotImplementedException();
                    }
                    else
                    {
                        AppendQueryExpressionCommand(ref commandTextStringBuilder, expressionElement, dbTypes, tableExpression);
                    }
                }
                else
                {
                    // TODO: Epxression Is null.
                }
            }

            string commandText = commandTextStringBuilder.ToString();
            commonCommand.CommandText = commandText;
            return commonCommand;
        }

        private void AppendQueryChildExpressionCommand(ref StringBuilder commandTextStringBuilder, RelationExpression relationExpression, Dictionary<string, System.Data.DbType> dbTypes, TableExpression tableExpression)
        {
            foreach (RelationExpression childExpression in relationExpression.Children)
            {
                AppendQueryExpressionCommand(ref commandTextStringBuilder, childExpression, dbTypes, tableExpression);
            }
        }

        private void AppendQueryExpressionCommand(ref StringBuilder commandTextStringBuilder, CommonCommand operationExpressionCommand)
        {
            commandTextStringBuilder.Append(operationExpressionCommand.CommandText);
        }

        private string GetQuerySortingExpressionString(SortingExpression sortingExpression)
        {
            return string.Format("<FieldRef Name='{0}' Ascending='{1}'/>", sortingExpression.ColumnName, sortingExpression.SortingDirection == SortingDirection.Ascending ? "TRUE" : "FALSE");
        }
    }
}
